#!/usr/bin/env python
# -*- coding:utf8 -*-
"""
Author : mafei
@Date   : 2019/11/30

"""

import time
import json
import os
import logging
import redis
import hashlib
logger = logging.getLogger("interactor")


def create_id_hash(hash_str):
    return hashlib.md5(hash_str).hexdigest()


class RedisBase(object):
    g_conn = None

    def __init__(self, namespace=None):
        self.prefix = namespace or ''

    @property
    def r_server(self):
        if not RedisBase.g_conn:
            RedisBase.g_conn = redis.Redis(host='127.0.0.1', port=6379,
                        decode_responses=True, db=3, password="mmonitor")  # host是redis主机，需要redis服务端和客户端都启动 redis默认端口是6379
        return RedisBase.g_conn

    def set_namespace(self, namespace):
        self.prefix = namespace

    def key_wrapper(self, k, namespace=None):
        return "{}:{}".format(namespace or self.prefix, k[-500:])

    def value_wrapper(self, v):
        param_type = type(v)
        if param_type in (str, int):
            return v
        elif param_type in (dict, list, set):
            return json.dumps(v, ensure_ascii=False)
        else:
            return v

    def expire(self, k, expire_time, namespace=None):
        return self.r_server.expire(self.key_wrapper(k, namespace=namespace), expire_time)

    def exists(self, k, namespace=None):
        return self.r_server.exists(self.key_wrapper(k, namespace=namespace))

    def keys(self, k, namespace=None):
        return self.r_server.keys(self.key_wrapper(k, namespace=namespace))

    def delete(self, k, namespace=None):
        return self.r_server.delete(self.key_wrapper(k, namespace=namespace))

    def delete_keys(self, keys):
        return self.r_server.delete(*keys)


class LockManager(object):
    """ Redis distributed locks """

    def __init__(self, connector, life_cycle=None):
        self.pid = os.getpid()
        self.r_server = connector
        # set life cycle for locks
        self.life_cycle = life_cycle or 10

    def key_wrapper(self, k):
        return "{}:{}".format('dlm', k[-500:])

    def acquire(self, k):
        # set k ,v in lock namespace with wrapper
        k = self.key_wrapper(k)
        v = self._get_certificate(k)
        if self.r_server.setnx(k, v):
            self.r_server.expire(k, self.life_cycle)
            return True
        else:
            return False

    def release(self, k):
        k = self.key_wrapper(k)
        if self.r_server.get(k) == self._get_certificate(k):
            self.r_server.delete(k)
            return True
        else:
            return False

    def is_locked(self, k):
        # return k status in namespace
        return True if self.r_server.exists(self.key_wrapper(k)) else False

    def _get_certificate(self, k):
        # generate certificate towards the given key according to algorithm
        # todo algorithm needs to be complex to diff varies processes in distributed machines
        return create_id_hash("{}{}".format(k, self.pid))


class CacheInteractor(RedisBase):
    def __init__(self, namespace=None, life_cycle=None):
        RedisBase.__init__(self, namespace)
        self.lock = LockManager(self.r_server)
        self.life_cycle = life_cycle or 2 * 24 * 3600

    def set(self, k, v, namespace=None, life_cycle=None):
        # if k is locked, return False
        if self.lock.is_locked(k):
            return False
        else:
            key_name = self.key_wrapper(k, namespace=namespace)
            self.r_server.set(key_name, self.value_wrapper(v),
                              ex=life_cycle or self.life_cycle)
            return True

    def set_with_lock(self, k, v, namespace=None, life_cycle=None):
        try:
            for i in range(10000):
                if self.lock.acquire(k):
                    break
                else:
                    time.sleep(0.001)
                    # print "is waiting for dlm lock"
            key_name = self.key_wrapper(k, namespace=namespace)
            self.r_server.set(key_name, self.value_wrapper(v),
                              ex=life_cycle or self.life_cycle)
            self.lock.release(k)
        except:
            return False
        return True

    def get(self, k, namespace=None):
        return self.r_server.get(self.key_wrapper(k, namespace=namespace))

    def exists(self, k, namespace=None):
        # print self.key_wrapper(k, namespace=namespace)
        result = self.r_server.exists(self.key_wrapper(k, namespace=namespace))
        if result:
            return result
        else:
            return None

    def get_json(self, k, namespace=None):
        res = self.r_server.get(self.key_wrapper(k, namespace=namespace))
        if res:
            try:
                res = json.loads(res)
            except:
                pass
        else:
            res = {}
        return res


class ListInteractor(RedisBase):
    def __init__(self, namespace=None, life_cycle=None):
        RedisBase.__init__(self, namespace)
        self.life_cycle = life_cycle or 2 * 24 * 3600

    def lpush(self, k, *v):
        key_name = self.key_wrapper(k)
        ret = self.r_server.lpush(key_name, *v)
        self.r_server.expire(key_name, self.life_cycle)
        return ret

    def ltrim(self, k, start, end):
        key_name = self.key_wrapper(k)
        return self.r_server.ltrim(key_name, start, end)

    def lrange(self, k, start, end):
        key_name = self.key_wrapper(k)
        return self.r_server.lrange(key_name, start, end)


class SetInteractor(RedisBase):
    def __init__(self, namespace=None, life_cycle=None):
        RedisBase.__init__(self, namespace)
        self.life_cycle = life_cycle or 2 * 24 * 3600

    def sadd(self, k, *v):
        key_name = self.key_wrapper(k)
        ret = self.r_server.sadd(key_name, *v)
        self.r_server.expire(key_name, self.life_cycle)
        return ret

    def sismeber(self, k, v):
        key_name = self.key_wrapper(k)
        return self.r_server.sismember(key_name, v)

    def smembers(self, k):
        key_name = self.key_wrapper(k)
        return self.r_server.smembers(key_name)

    def srem(self, k, *v):
        key_name = self.key_wrapper(k)
        return self.r_server.srem(key_name, *v)
